package com.xu.CAS;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * ABA问题描述：
 *    如果一个变量 V 初次读取的时候是 A 值，并且在准备赋值的时候检查到它仍然为 A 值，那就能说明它的值没有被其他线程改变过了吗？
 *    如果在这段期间它的值曾经被改成 B，后来又被改回为 A，那CAS操作就会误人误它从来没有被改变过。这个漏洞称为 CAS 操作的 “ABA问题”
 * @author xudenghui
 * @create 2023-02-27 11:57
 **/
public class ABADemo {

    public static void main(String[] args) {
       // test1();
        test2();
    }

    static AtomicInteger atomicInteger = new AtomicInteger(100);
    private static void test1() {
        new Thread(()->{
            atomicInteger.compareAndSet(100, 200);
            //暂停10豪秒
            try {
                TimeUnit.MILLISECONDS.sleep(10);
            }catch (InterruptedException e){
                e.printStackTrace();
            }finally {
                atomicInteger.compareAndSet(200, 100);
            }
        },"t1").start();


        new Thread(()->{
            //暂停200豪秒
            try {
                TimeUnit.MILLISECONDS.sleep(10);
            }catch (InterruptedException e){
                e.printStackTrace();
            }finally {
                System.out.println( atomicInteger.compareAndSet(100, 500) +"\t"+atomicInteger.get());
            }
        },"tw").start();
    }


    static AtomicStampedReference<Integer> stampedReference = new AtomicStampedReference<>(100, 1);
    private static void test2() {
        new Thread(()->{
           int stamp = stampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+"\t首次版本号："+stamp);
            try {
                TimeUnit.MILLISECONDS.sleep(500);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            stampedReference.compareAndSet(100, 200, stampedReference.getStamp(),stampedReference.getStamp()+1);
            System.out.println(Thread.currentThread().getName()+"\t 2次版本号："+stamp);
            stampedReference.compareAndSet(200, 100, stampedReference.getStamp(),stampedReference.getStamp()+1);


        },"t1").start();


        new Thread(()->{
            int stamp = stampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+"\t"+"首次版本号："+stamp);
            // 暂停1000毫秒，保证t2线程初始化拿到的版本号和t1一样
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            boolean b = stampedReference.compareAndSet(100, 500, stamp, stampedReference.getStamp() + 1);
            System.out.println(b + "\t" + stampedReference.getReference() + "\t" + stampedReference.getStamp());
        },"t2").start();
    }
}
